package org.eoti.elec;

/**
 * The MIT License
 * <p/>
 * Copyright (c) 2009 Malachi de AElfweald (malachid@gmail.com)
 * <p/>
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * <p/>
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 * Software.
 * <p/>
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

import com.sun.org.apache.bcel.internal.classfile.Unknown;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class TruthTable
implements TickListener
{
	protected DigitalComponent component;
	protected PinNumberComparator comparator;
	protected int index = 0;
	protected PrintStream out;
	protected ArrayList<Pin> input, output;
	protected String fmtIdx, fmtPin;
	protected String fmtSplit = " |";
	protected boolean done = false;

	public TruthTable(DigitalComponent component, PrintStream out)
	{
		this.component = component;
		this.comparator = new PinNumberComparator();
		this.out = out;

		input = new ArrayList<Pin>(component.inputPins);
		output = new ArrayList<Pin>(component.outputPins);

		int maxRuns = (int) Math.pow(2.0, ((""+input.size()).length()));
		fmtIdx = "%" + maxRuns + "d" + fmtSplit;
		int max = 0;
		for(Pin pin : input)
			max = Math.max(max, pin.getName().length());

		for(Pin pin : output)
			max = Math.max(max, pin.getName().length());

		fmtPin = " %" + (max+1) + "s";

		out.format("TruthTable: %s\n", component.getName());
		out.format("%" + maxRuns + "s" + fmtSplit, " ");

		for(Pin pin : input)
			out.format(fmtPin, pin.getName());

		out.format(fmtSplit);

		for(Pin pin : output)
			out.format(fmtPin, pin.getName());

		out.format("\n");

		for(Pin pin : input)
			pin.setState(State.Low);

		/**
		 * need to set initial state and output truth table header
		 */
		TickGenerator.getInstance().addTickListener(this);
	}

	public boolean isDone(){return done;}

	public void tick()
	{
		if(done) TickGenerator.getInstance().removeTickListener(this);

		/**
		 * instead of checking whether the buffer has been copied over,
		 * we just won't do anything until after we know it has been
		 */
		if(component.currentDelay != 0) return;
		/**
		 * Make sure they are in numeric order
		 */
		Collections.sort(input, comparator);
		Collections.sort(output, comparator);

		out.format(fmtIdx, (index++));
		for(Pin pin : input)
		{
			switch(pin.getState())
			{
				case High:
					out.format(fmtPin, "1");
					break;
				case Low:
					out.format(fmtPin, "0");
					break;
				case Unknown:
					out.format(fmtPin, "?");
					break;
			}
		}

		out.format(fmtSplit);

		for(Pin pin : output)
		{
			switch(pin.getState())
			{
				case High:
					out.format(fmtPin, "1");
					break;
				case Low:
					out.format(fmtPin, "0");
					break;
				case Unknown:
					out.format(fmtPin, "?");
					break;
			}
		}

		out.format("\n");
		increment();
		for(Pin pin : input)
		{
			if(pin.getState() != State.Low) return;
		}

		done = true;
	}

	protected void increment()
	{
		for(int pos=(input.size()-1); pos >= 0; pos--)
		{
			Pin pin = input.get(pos);
			switch(pin.getState())
			{
				case High:
					pin.setState(State.Low); // and continue to next pin
					break;
				case Low:
					pin.setState(State.High); // and return
					return;
				case Unknown:
					return;
			}
		}
	}

	class PinNumberComparator
	implements Comparator<Pin>
	{
		public int compare(Pin pin1, Pin pin2)
		{
			return pin1.getPinNumber() - pin2.getPinNumber();
		}
	}
}
